Manipulação de Dados com Pandas e GeoPandas
Saia das estruturas básicas e explore bibliotecas poderosas para ler, limpar e analisar dados tabulares e geoespaciais antes de visualizá-los em mapas interativos.
Você vai aprender a
- ▹Ler e explorar arquivos CSV com o Pandas.
- ▹Converter e reprojetar dados espaciais com GeoPandas.
- ▹Executar consultas espaciais para responder perguntas de localização.
Arquivos necessários para acompanhar a aula
Dataset de referência
Contém dados fictícios de balneabilidade para algumas praias de Florianópolis, incluindo nome, latitude, longitude e situação, ideais para as consultas com Pandas e GeoPandas.
Shapefile oficial do IBGE
Arquivo SC_Municipios_2022 com as geometrias municipais atualizadas. O download ocorre a partir dos servidores públicos do IBGE.
arquivos/SC_Municipios_2022/.
.shp, .dbf, .shx, .prj etc.) juntos na mesma pasta. O GeoPandas precisa de todos eles para montar a camada corretamente.
1. Introdução: Por que precisamos de mais que listas e dicionários?
Listas e dicionários nos ajudaram a organizar dados básicos em Python, como uma coleção de praias com seus atributos.
dados_praias = [
{"Praia": "Praia da Barra", "Longitude": -48.573928, "Latitude": -27.600218, "Situação": "Boa"},
{"Praia": "Praia Brava", "Longitude": -48.574293, "Latitude": -27.600404, "Situação": "Boa"},
# ... e assim por diante
]
Lembre-se da nossa analogia das "caixinhas" para variáveis. Listas e dicionários nos permitem organizar várias delas, mas carregar cada caixinha individualmente se torna impossível quando lidamos com milhares de registros. Precisamos de ferramentas melhores: um carrinho para transportar dezenas de caixas de uma vez, ou uma empilhadeira para organizar um armazém inteiro. Para escalar o trabalho com dados, o princípio é o mesmo. Recorremos a ferramentas feitas sob medida para análise de dados: Pandas funciona como nosso carrinho de mão superpoderoso para tabelas, e GeoPandas é a nossa empilhadeira especializada para dados espaciais.
2. Instalação das bibliotecas necessárias
Antes de trabalhar com dados tabulares e espaciais, precisamos instalar as bibliotecas Pandas e GeoPandas. Vamos usar o pip, que é o gerenciador de pacotes oficial do Python.
2.1 Instalando o Pandas
O Pandas é relativamente simples de instalar. Abra o terminal (ou prompt de comando) e execute:
pip install pandas
2.2 Instalando o GeoPandas
O GeoPandas depende de várias bibliotecas geoespaciais. A instalação pode ser feita de duas formas:
Opção 1: Instalação direta (recomendada)
pip install geopandas
Esta opção instala automaticamente todas as dependências necessárias.
Opção 2: Se houver problemas de dependência
pip install fiona shapely pyproj matplotlib
pip install geopandas
Instala as dependências principais (incluindo matplotlib para gráficos) primeiro, depois o GeoPandas.
2.3 Verificando a instalação
Para confirmar que tudo foi instalado corretamente, teste as importações:
import pandas as pd
import geopandas as gpd
print(f"Pandas versão: {pd.__version__}")
print(f"GeoPandas versão: {gpd.__version__}")
print("✅ Todas as bibliotecas instaladas com sucesso!")
3. Pandas: Sua planilha superpoderosa no Python
Pandas é a biblioteca central para manipular estruturas tabulares em Python. Ele concentra grandes volumes de dados em DataFrames, oferecendo operações vetorizadas eficientes. Se pensarmos rapidamente na analogia das caixinhas, ele agrupa e empilha essas unidades em um carrinho único, pronto para circular pelo fluxo de análise. Podemos enxergar o processo principal nestas três etapas:
Importar dados em lote
Lê arquivos CSV, planilhas, bancos SQL e formatos columnar como Parquet, centralizando tudo em um DataFrame com colunas nomeadas e índices consistentes.
Normalizar tipos e colunas
Detecta automaticamente valores numéricos, categóricos ou temporais, permitindo conversões, limpeza e validação de dados com poucas linhas.
Explorar e analisar rapidamente
Filtra, agrupa, calcula estatísticas e integra com visualizações, respondendo perguntas analíticas de milhares de registros em segundos.
Em essência, o Pandas viabiliza pipelines de dados escaláveis, substituindo laços manuais por operações declarativas, legíveis e de alto desempenho.
3.1 A estrutura principal: o DataFrame
Imagine o DataFrame como a estrela do show no Pandas. A maneira mais fácil de entender essa estrutura é vê-la como uma tabela ou planilha digital superpoderosa.
Pense assim: cada DataFrame é como uma folha de Excel, mas que você controla com código! Ele organiza suas "caixinhas" de dados (suas variáveis) em duas dimensões perfeitas:
- Linhas: cada linha é um registro completo. Pense nos dados de uma pessoa, um produto ou um evento. É o seu item individual.
- Colunas: cada coluna é um tipo específico de informação. Por exemplo, uma coluna pode ser "Nome", outra "Idade", "Preço" ou "Data". É a categoria da sua informação.
O segredo das colunas está nos rótulos e nos tipos. Cada coluna tem um rótulo claro (o nome que você dá, como "Nome do Cliente" ou "Valor Total") e, o mais importante, armazena dados de um tipo apropriado.
Veja um exemplo: uma coluna "Idade" só terá números inteiros. Já uma coluna "Produto" terá textos, e "Data da Venda" terá o formato certinho de data.
Essa organização é crucial! Não só facilita a leitura e a interpretação dos dados, mas também garante que o Pandas realize operações corretamente. Afinal, você não tentaria somar um "nome" com um "preço", não é? O DataFrame cuida disso para você.
3.2 Lendo dados de um arquivo CSV
Carregar dados tabulares é simples com pd.read_csv(). Para esta atividade, utilize o arquivo Pontos.csv que se encontra no início desta página.
# 1. Importar a biblioteca pandas
import pandas as pd # "pd" é o apelido padrão
# 2. Ler o arquivo CSV e carregar os dados em um DataFrame
try:
df_praias = pd.read_csv('arquivos/Pontos.csv')
# 3. Explorar os dados que foram carregados
print("--- As 5 primeiras linhas do nosso DataFrame ---")
print(df_praias.head()) # .head() mostra as primeiras 5 linhas
print("\n--- Informações sobre as colunas e os tipos de dados ---")
print(df_praias.info()) # .info() resume a estrutura do DataFrame
except FileNotFoundError:
print("Erro: O arquivo 'arquivos/Pontos.csv' não foi encontrado.")
print("Certifique-se de que ele está na pasta 'arquivos/' do seu projeto.")
3.3 Operações básicas com DataFrames
Depois de carregar os dados, o Pandas simplifica consultas, cálculos e filtragens que antes exigiriam muitos laços for.
import pandas as pd
try:
df_praias = pd.read_csv('arquivos/Pontos.csv')
# a) Selecionar uma coluna específica (retorna uma Series)
nomes_das_praias = df_praias['Praia']
print("--- Nomes de todas as praias ---")
print(nomes_das_praias)
# b) Calcular estatísticas básicas de uma coluna numérica
latitude_media = df_praias['Latitude'].mean()
print(f"\n--- A latitude média é: {latitude_media:.4f} ---")
# c) Filtrar o DataFrame pela Situação
praias_ruins = df_praias[df_praias['Situação'] == 'Ruim']
print("\n--- Praias com situação 'Ruim' ---")
print(praias_ruins)
except FileNotFoundError:
print("Erro: O arquivo 'arquivos/Pontos.csv' não foi encontrado.")
Note como cálculos e filtros robustos cabem em poucas linhas, liberando tempo para análises mais profundas.
4. GeoPandas: adicionando inteligência geográfica
GeoPandas expande o Pandas para entender colunas com latitudes e longitudes como geometrias reais. Tudo isso apoiado pela biblioteca pyproj para transformações de coordenadas.
4.1 Lendo um arquivo Shapefile
Shapefiles são formatos clássicos do geoprocessamento, contendo atributos e geometria. GeoPandas lê essas estruturas com read_file().
import geopandas as gpd
# Caminho para o arquivo .shp (extraído do ZIP do IBGE)
caminho_shapefile = 'arquivos/SC_Municipios_2022/SC_Municipios_2022.shp'
try:
gdf_municipios = gpd.read_file(caminho_shapefile)
print("--- As 5 primeiras linhas do nosso GeoDataFrame ---")
print(gdf_municipios.head())
print("\n--- Informações sobre o GeoDataFrame ---")
gdf_municipios.info()
# Repare na coluna "geometry", exclusiva dos GeoDataFrames
except Exception as e:
print(f"Não foi possível ler o arquivo Shapefile: {e}")
print("Verifique se o caminho está correto e se o arquivo não está corrompido.")
4.2 Visualização rápida: a prova real do geoprocessamento
Depois de carregar ou criar um GeoDataFrame, nada melhor do que desenhar um mapa para validar se os dados fazem sentido. O método .plot() usa Matplotlib por trás dos panos e gera uma visualização estática imediata, perfeita para conferir o Sistema de Referência de Coordenadas (CRS), detectar geometrias distorcidas ou confirmar se o recorte espacial bate com o esperado.
Começamos com o cenário mais simples: carregar o shapefile e chamar plot() sem ajustes extras. Ainda assim, vale sempre adicionar um título e rótulos nos eixos para contextualizar.
import geopandas as gpd
import matplotlib.pyplot as plt
caminho_shapefile = 'shapefiles/SC_Municipios_2022.shp'
try:
# Carregar o shapefile usando GeoPandas
gdf_municipios = gpd.read_file(caminho_shapefile)
# Plotar o mapa simples
gdf_municipios.plot()
plt.title("Mapa Simples dos Municípios de SC") # adicionar título
plt.xlabel("Longitude") # adicionar rótulo ao eixo x
plt.ylabel("Latitude") # adicionar rótulo ao eixo y
plt.show() # exibir o gráfico
except Exception as e:
print(f"Ocorreu um erro ao plotar o mapa: {e}")
Quando precisamos combinar camadas, podemos desenhar múltiplos GeoDataFrames no mesmo eixo (ax). Abaixo filtramos apenas o município de Florianópolis e sobrepomos os pontos de balneabilidade do arquivo Pontos.csv, todos convertidos para WGS84 (EPSG:4326) para garantir alinhamento.
import geopandas as gpd
import pandas as pd
import matplotlib.pyplot as plt
caminho_municipios = 'shapefiles/SC_Municipios_2022.shp'
caminho_pontos = 'arquivos/Pontos.csv'
try:
gdf_municipios = gpd.read_file(caminho_municipios)
# Filtrar apenas Florianópolis e garantir que esteja em WGS84
gdf_floripa = gdf_municipios[gdf_municipios['NM_MUN'] == 'Florianópolis']
if gdf_floripa.empty:
raise ValueError('Não há registros de Florianópolis na camada municipal.')
gdf_floripa = gdf_floripa.to_crs('EPSG:4326')
# Transformar o CSV em GeoDataFrame de pontos
df_pontos = pd.read_csv(caminho_pontos)
gdf_pontos = gpd.GeoDataFrame(
df_pontos,
geometry=gpd.points_from_xy(df_pontos['Longitude'], df_pontos['Latitude']),
crs='EPSG:4326'
)
fig, ax = plt.subplots(figsize=(9, 8))
gdf_floripa.plot(
ax=ax,
color='#bbf7d0',
edgecolor='#047857',
linewidth=0.8,
label='Florianópolis'
)
gdf_pontos.plot(
ax=ax,
color='#dc2626',
markersize=45,
marker='o',
label='Pontos monitorados',
alpha=0.85
)
ax.set_title('Pontos de balneabilidade dentro de Florianópolis', fontsize=15, fontweight='bold')
ax.set_xlabel('Longitude')
ax.set_ylabel('Latitude')
ax.legend()
ax.set_aspect('equal')
plt.show()
except Exception as e:
print(f"Ocorreu um erro ao montar a figura: {e}")
Customizando a visualização
O .plot() aceita parâmetros úteis para criar mapas temáticos simples direto no notebook ou no script.
color e edgecolor
Controlam o preenchimento e o contorno dos polígonos.
figsize
Define o tamanho da figura ao usar plt.subplots().
column
Aplica um coroplético simples com base nos valores de uma coluna.
cmap
Escolhe a paleta de cores usada no coroplético.
legend
Mostra ou oculta a legenda quando usamos o parâmetro column.
Na sequência, a seção 4.3 mostra como manipular e converter formatos a partir desses dados já conferidos. Se quiser ver um exemplo de sobreposição de camadas com pontos e polígonos, confira o bloco opcional ao final desta parte (seção 4.10).
4.3 Manipulação e conversão de formatos
GeoPandas também funciona como um tradutor entre formatos espaciais. Converter Shapefile para GeoJSON, por exemplo, é direto.
import geopandas as gpd
caminho_shapefile = 'arquivos/SC_Municipios_2022/SC_Municipios_2022.shp'
caminho_saida_geojson = 'saidas/municipios_sc.geojson'
try:
gdf_municipios = gpd.read_file(caminho_shapefile)
gdf_municipios.to_file(caminho_saida_geojson, driver='GeoJSON')
print(f"Arquivo GeoJSON salvo com sucesso em: {caminho_saida_geojson}")
except Exception as e:
print(f"Ocorreu um erro durante o processo: {e}")
4.4 Transformando sistemas de coordenadas
Converter entre sistemas geográficos (graus) e projetados (metros) é essencial para cálculos de área e distância. GeoPandas simplifica a reprojeção via .to_crs().
import geopandas as gpd
caminho_shapefile = 'shapefiles/SC_Municipios_2022.shp'
try:
# Carregar o shapefile usando GeoPandas
gdf_municipios = gpd.read_file(caminho_shapefile)
gdf_utm = gdf_municipios # ponto de partida caso o CRS já esteja correto
# verificar se o CRS está definido
if gdf_municipios.crs is None:
# criar uma exceção caso o CRS não esteja definido
raise ValueError("O GeoDataFrame não possui um CRS definido. Por favor, defina o CRS antes de reprojetar.")
# Reprojetar para UTM (EPSG:31982) se o CRS atual for diferente
elif gdf_municipios.crs.to_string() != "EPSG:31982":
print("Reprojetando para UTM (EPSG:31982)...")
gdf_utm = gdf_municipios.to_crs("EPSG:31982")
# Conferir o resultado
print("\n--- Geometrias após a conversão para UTM (coordenadas em metros) ---")
print(gdf_utm.head()) # exibir as primeiras linhas do GeoDataFrame reprojetado
except Exception as e:
print(f"Ocorreu um erro durante a reprojeção: {e}")
4.5 Exportando para GeoPackage e padronizando o CRS
Uma vez que o CRS está conferido (seção 4.4), podemos entregar os dados em formatos modernos. O GeoPackage (.gpkg) guarda múltiplas camadas no mesmo arquivo e aceita geometrias em qualquer projeção. O snippet abaixo reprojeta tudo para SIRGAS 2000 / UTM zona 22S (EPSG:31982) e exporta cada camada definida em uma lista fixa.
import geopandas as gpd
from pathlib import Path
crs_destino = 'EPSG:31982' # SIRGAS 2000 / UTM 22S
destino_gpkg = Path('saidas/base_geoespacial.gpkg')
camadas = [
{
'origem': 'arquivos/SC_Municipios_2022/SC_Municipios_2022.shp',
'layer': 'municipios_sc'
},
{
'origem': 'arquivos/exemplos/areas_preservacao.shp',
'layer': 'areas_preservacao'
}
]
destino_gpkg.parent.mkdir(parents=True, exist_ok=True)
for item in camadas:
origem = Path(item['origem'])
layer = item['layer']
try:
gdf = gpd.read_file(origem)
gdf_utm = gdf.to_crs(crs_destino)
gdf_utm.to_file(destino_gpkg, layer=layer, driver='GPKG')
print(f"✅ Camada '{layer}' exportada para {destino_gpkg.name} em {crs_destino}")
except Exception as erro:
print(f"⚠️ Não foi possível processar {origem.name}: {erro}")
Adapte a lista camadas aos arquivos do seu projeto — substitua, por exemplo, arquivos/exemplos/areas_preservacao.shp pelo caminho real da sua camada temática. O padrão é sempre ler, reprojetar e gravar no mesmo loop; assim o time economiza cliques e mantém todas as saídas alinhadas ao sistema oficial da disciplina.
4.6 Transformando DataFrame em GeoDataFrame
Do CSV ao mapa: dando consciência espacial aos dados
Uma das tarefas mais corriqueiras do geoprocessamento é transformar dados tabulares em camadas espaciais. Planilhas de clientes, listas de ocorrências ou arquivos .csv exportados do GNSS chegam cheios de coordenadas, mas para o Pandas são apenas números. Para desbloquear cálculos de distância, buffers ou consultas espaciais, precisamos converter o DataFrame em GeoDataFrame, a estrutura que entende geometrias.
Pense nesse processo como uma tradução em três atos:
- Carregar os dados tabulares: usamos o Pandas para organizar o arquivo em linhas e colunas.
- Construir a geometria: a partir das colunas de coordenadas, geramos objetos
Pointque representam cada registro no espaço. - Criar o GeoDataFrame: combinamos dados e geometria e informamos o Sistema de Referência de Coordenadas (CRS) correto — etapa crucial para que os pontos apareçam no lugar certo do mundo.
Imagine um arquivo levantamento_rtk.csv, exportado de um receptor Topcon após o trabalho de campo:
Ponto,Leste(X),Norte(Y),Elevacao,Descricao
P01,754231.56,6941234.89,12.34,Poste
P02,754235.12,6941238.45,12.56,Meio-fio
P03,754240.03,6941242.18,12.61,Esquina
O GeoPandas oferece a função points_from_xy(), que lê diretamente as colunas de coordenadas e monta os pontos para nós. Em seguida, informamos que esses valores estão em SIRGAS 2000 / UTM zona 22S (EPSG:31982) e ganhamos uma camada espacial pronta para análise.
import pandas as pd
import geopandas as gpd
try:
df = pd.read_csv('arquivos/levantamento_rtk.csv')
geometria = gpd.points_from_xy(df['Leste(X)'], df['Norte(Y)'])
gdf = gpd.GeoDataFrame(df, geometry=geometria, crs='EPSG:31982')
print('GeoDataFrame criado com sucesso!')
print(gdf.head())
print(f'CRS informado: {gdf.crs}')
except FileNotFoundError:
print('Arquivo levantamento_rtk.csv não encontrado. Verifique o caminho.')
except Exception as erro:
print(f'Erro ao criar o GeoDataFrame: {erro}')
A partir daqui, o conjunto está apto a receber buffers, participar de spatial join, ser visualizado com .plot() ou exportado para GeoPackage (seção 4.5). O segredo é garantir que o CRS esteja correto desde o início.
4.7 Spatial Joins (Junções Espaciais)
As junções espaciais permitem cruzar dados de pontos com polígonos, respondendo perguntas como "a qual município pertence cada praia?" ou "quantas praias estão em cada cidade?". É uma operação fundamental no geoprocessamento.
import pandas as pd
import geopandas as gpd
from shapely.geometry import Point
try:
# 1. Criar GeoDataFrame das praias
df_praias = pd.read_csv('arquivos/Pontos.csv')
geometry_praias = [Point(xy) for xy in zip(df_praias['Longitude'], df_praias['Latitude'])]
gdf_praias = gpd.GeoDataFrame(df_praias, geometry=geometry_praias, crs='EPSG:4326')
# 2. Carregar shapefile dos municípios
gdf_municipios = gpd.read_file('arquivos/SC_Municipios_2022/SC_Municipios_2022.shp')
# 3. Realizar a junção espacial
# Cada praia será associada ao município que a contém
praias_com_municipio = gpd.sjoin(gdf_praias, gdf_municipios, how='left', predicate='within')
print("--- Praias com informações do município ---")
print(praias_com_municipio[['Praia', 'Situação', 'NM_MUN']].head())
# 4. Análise: quantas praias por município?
contagem_por_municipio = praias_com_municipio.groupby('NM_MUN').size()
print(f"\n--- Praias por município ---")
print(contagem_por_municipio)
# 5. Análise: situação das praias por município
situacao_por_municipio = praias_com_municipio.groupby(['NM_MUN', 'Situação']).size().unstack(fill_value=0)
print(f"\n--- Situação das praias por município ---")
print(situacao_por_municipio)
except Exception as e:
print(f"Erro na junção espacial: {e}")
4.8 Operações geométricas: criando buffers
Buffers são áreas de influência ao redor de geometrias. São úteis para análises de proximidade, como "quais praias estão a menos de 500 metros de uma escola?" ou "qual a área de impacto de um empreendimento?".
import pandas as pd
import geopandas as gpd
from shapely.geometry import Point
import matplotlib.pyplot as plt
try:
# 1. Criar GeoDataFrame das praias
df_praias = pd.read_csv('arquivos/Pontos.csv')
geometry_praias = [Point(xy) for xy in zip(df_praias['Longitude'], df_praias['Latitude'])]
gdf_praias = gpd.GeoDataFrame(df_praias, geometry=geometry_praias, crs='EPSG:4326')
# 2. Reprojetar para um sistema em metros (necessário para buffers em metros)
gdf_praias_utm = gdf_praias.to_crs('EPSG:31982') # UTM Zone 22S
# 3. Criar buffers de 500 metros ao redor de cada praia
gdf_praias_utm['buffer_500m'] = gdf_praias_utm.geometry.buffer(500)
# 4. Criar um novo GeoDataFrame só com os buffers
gdf_buffers = gdf_praias_utm.copy()
gdf_buffers['geometry'] = gdf_buffers['buffer_500m']
gdf_buffers = gdf_buffers.drop('buffer_500m', axis=1)
# 5. Reprojetar de volta para visualização
gdf_praias_plot = gdf_praias_utm.to_crs('EPSG:4326')
gdf_buffers_plot = gdf_buffers.to_crs('EPSG:4326')
print(f"Criados {len(gdf_buffers)} buffers de 500m")
# 6. Visualizar (opcional)
fig, ax = plt.subplots(figsize=(12, 8))
gdf_buffers_plot.plot(ax=ax, color='lightblue', alpha=0.5, label='Buffer 500m')
gdf_praias_plot.plot(ax=ax, color='red', markersize=50, label='Praias')
ax.set_title('Praias e suas áreas de influência (500m)')
ax.legend()
plt.show()
# 7. Calcular área total dos buffers
area_total_buffers = gdf_buffers.geometry.area.sum()
print(f"Área total dos buffers: {area_total_buffers:.2f} m²")
except Exception as e:
print(f"Erro ao criar buffers: {e}")
4.9 Introdução a relações topológicas (consultas espaciais)
Consultas espaciais respondem perguntas sobre interseções, contenções e vizinhança entre objetos geográficos. GeoPandas usa internamente a biblioteca Shapely para esses cálculos.
import geopandas as gpd
from shapely.geometry import Point
caminho_shapefile = 'arquivos/SC_Municipios_2022/SC_Municipios_2022.shp'
try:
gdf_municipios = gpd.read_file(caminho_shapefile)
# 1. Criar um ponto para o IFSC (longitude, latitude)
ponto_ifsc = Point(-48.54235, -27.59397)
# 2. Consulta espacial: qual município contém o ponto?
municipio_ifsc = gdf_municipios[gdf_municipios.contains(ponto_ifsc)]
# 3. Exibir o resultado
if not municipio_ifsc.empty:
nome_municipio = municipio_ifsc['NM_MUN'].iloc[0]
print(f"O ponto do IFSC está localizado no município de: {nome_municipio}")
else:
print("Não foi encontrado nenhum município contendo o ponto especificado.")
except Exception as e:
print(f"Ocorreu um erro durante a consulta espacial: {e}")
4.10 Operações de Sobreposição (Overlay)
Enquanto as junções espaciais (sjoin) enriquecem os atributos de uma camada com base na relação espacial com outra, as operações de sobreposição (overlay) criam novas geometrias a partir da interação entre as camadas.
Imagine combinar municípios e áreas de preservação ambiental. Com overlay, você responde perguntas como:
- Interseção (
intersection): Quais porções de cada área de preservação estão dentro de um município? O resultado gera novos polígonos apenas nas áreas de sobreposição. - União (
union): Qual geometria representa a combinação de municípios e áreas preservadas? O resultado funde as duas camadas em uma única malha. - Diferença (
difference): Qual parte do município fica fora das áreas de preservação? O resultado mantém a primeira camada com as regiões da segunda "subtraídas".
Essas operações são a base de análises espaciais mais complexas e estão disponíveis no GeoPandas por meio da função gpd.overlay(). Basta indicar as duas camadas, escolher o parâmetro how (como 'intersection', 'union' ou 'difference') e garantir que ambas estejam no mesmo CRS.
Exemplo opcional: sobrepondo pontos das praias a Florianópolis
Use o .plot() em conjunto com o Matplotlib para compor camadas. O script destaca Florianópolis e plota as praias por cima, ótimo para validar se a distribuição espacial está coerente antes de partir para o Folium no capítulo 8.
import geopandas as gpd
import pandas as pd
from shapely.geometry import Point
import matplotlib.pyplot as plt
try:
gdf_municipios = gpd.read_file('arquivos/SC_Municipios_2022/SC_Municipios_2022.shp')
df_praias = pd.read_csv('arquivos/Pontos.csv')
geometry = [Point(xy) for xy in zip(df_praias['Longitude'], df_praias['Latitude'])]
gdf_praias = gpd.GeoDataFrame(df_praias, geometry=geometry, crs='EPSG:4326')
gdf_municipios = gdf_municipios.to_crs(gdf_praias.crs)
florianopolis = gdf_municipios[gdf_municipios['NM_MUN'] == 'Florianópolis']
fig, ax = plt.subplots(figsize=(10, 10))
florianopolis.plot(ax=ax, color='#f3f4f6', edgecolor='#0f172a', linewidth=0.8)
gdf_praias.plot(ax=ax, marker='o', color='#1d4ed8', markersize=60, label='Praias monitoradas')
ax.set_title('Distribuição das praias monitoradas em Florianópolis', fontsize=16)
ax.set_axis_off()
ax.legend()
plt.show()
except Exception as e:
print(f"Ocorreu um erro: {e}")
Quer avançar? Troque as praias por buffers (seção 4.8) ou aplique gpd.overlay() para descobrir qual parte dos buffers está dentro de áreas de preservação.
5. Laboratório prático: Consolidando múltiplos shapefiles em GeoPackage
Neste laboratório, vamos criar um fluxo completo que simula uma situação real: você recebeu múltiplos shapefiles de diferentes regiões e precisa consolidá-los em um único arquivo GeoPackage (.gpkg) padronizado, incluindo reprojeção para UTM.
5.1 Preparação do ambiente
Primeiro, vamos organizar nossa estrutura de pastas e preparar alguns dados de exemplo:
# Estrutura de pastas recomendada
projeto_geo/
├── arquivos/
│ ├── shapefiles_originais/
│ │ ├── municipios_SC/
│ │ ├── municipios_PR/
│ │ └── municipios_RS/
│ └── dados_processados/
├── saidas/
└── scripts/
5.2 Script principal do laboratório
Vamos criar um script que automatiza todo o processo de consolidação:
import geopandas as gpd
import pandas as pd
import os
from pathlib import Path
import warnings
warnings.filterwarnings('ignore')
def consolidar_shapefiles_para_geopackage():
"""
Consolida múltiplos shapefiles em um único GeoPackage
com reprojeção padronizada para UTM
"""
# === CONFIGURAÇÃO INICIAL ===
pasta_shapefiles = Path('arquivos/shapefiles_originais')
arquivo_saida = Path('saidas/dados_consolidados.gpkg')
crs_destino = 'EPSG:31982' # SIRGAS 2000 / UTM zone 22S
# Criar pasta de saída se não existir
arquivo_saida.parent.mkdir(parents=True, exist_ok=True)
print("🚀 Iniciando consolidação de shapefiles...")
print(f"📁 Pasta de origem: {pasta_shapefiles}")
print(f"📦 Arquivo de destino: {arquivo_saida}")
print(f"🗺️ Sistema de coordenadas destino: {crs_destino}")
print("-" * 50)
# === DESCOBRIR TODOS OS SHAPEFILES ===
shapefiles_encontrados = []
# Procurar recursivamente por arquivos .shp
for arquivo_shp in pasta_shapefiles.rglob('*.shp'):
shapefiles_encontrados.append(arquivo_shp)
if not shapefiles_encontrados:
print("❌ Nenhum shapefile encontrado na pasta especificada!")
return
print(f"📋 Encontrados {len(shapefiles_encontrados)} shapefiles:")
for i, arquivo in enumerate(shapefiles_encontrados, 1):
print(f" {i}. {arquivo.name} ({arquivo.parent.name})")
print("-" * 50)
# === PROCESSAR CADA SHAPEFILE ===
dados_consolidados = []
log_processamento = []
for i, arquivo_shp in enumerate(shapefiles_encontrados, 1):
try:
print(f"⚙️ Processando {i}/{len(shapefiles_encontrados)}: {arquivo_shp.name}")
# Carregar o shapefile
gdf = gpd.read_file(arquivo_shp)
# Informações básicas
crs_original = gdf.crs
num_features = len(gdf)
print(f" 📊 Features: {num_features}")
print(f" 🗺️ CRS original: {crs_original}")
# Reprojetar se necessário
if gdf.crs != crs_destino:
print(f" 🔄 Reprojetando para {crs_destino}...")
gdf = gdf.to_crs(crs_destino)
# Adicionar metadados de origem
gdf['arquivo_origem'] = arquivo_shp.name
gdf['pasta_origem'] = arquivo_shp.parent.name
gdf['data_processamento'] = pd.Timestamp.now().strftime('%Y-%m-%d %H:%M:%S')
# Adicionar à lista de dados consolidados
dados_consolidados.append(gdf)
# Log do processamento
log_processamento.append({
'arquivo': arquivo_shp.name,
'pasta': arquivo_shp.parent.name,
'crs_original': str(crs_original),
'num_features': num_features,
'status': 'sucesso'
})
print(f" ✅ Processado com sucesso!")
except Exception as e:
print(f" ❌ Erro ao processar: {e}")
log_processamento.append({
'arquivo': arquivo_shp.name,
'pasta': arquivo_shp.parent.name,
'crs_original': 'erro',
'num_features': 0,
'status': f'erro: {str(e)}'
})
if not dados_consolidados:
print("❌ Nenhum shapefile foi processado com sucesso!")
return
print("-" * 50)
print("🔗 Consolidando dados...")
# === CONSOLIDAR TODOS OS DADOS ===
try:
# Concatenar todos os GeoDataFrames
gdf_final = gpd.GeoDataFrame(pd.concat(dados_consolidados, ignore_index=True))
# Garantir que o CRS está correto
gdf_final.crs = crs_destino
# Estatísticas finais
total_features = len(gdf_final)
arquivos_processados = len([log for log in log_processamento if log['status'] == 'sucesso'])
print(f"📊 Resultado da consolidação:")
print(f" ✅ Arquivos processados: {arquivos_processados}/{len(shapefiles_encontrados)}")
print(f" 📈 Total de features: {total_features}")
print(f" 🗺️ CRS final: {gdf_final.crs}")
print(f" 📐 Extent (bounds): {gdf_final.total_bounds}")
# === SALVAR NO GEOPACKAGE ===
print("-" * 50)
print("💾 Salvando no GeoPackage...")
# Salvar dados principais
gdf_final.to_file(arquivo_saida, layer='dados_consolidados', driver='GPKG')
# Criar DataFrame com log de processamento
df_log = pd.DataFrame(log_processamento)
# Salvar log como tabela adicional (sem geometria)
# Converter para GeoDataFrame vazio apenas para usar to_file
gdf_log = gpd.GeoDataFrame(df_log)
gdf_log.to_file(arquivo_saida, layer='log_processamento', driver='GPKG')
print(f"✅ Dados salvos com sucesso em: {arquivo_saida}")
print(f"📋 Camadas criadas:")
print(f" • dados_consolidados: {total_features} features")
print(f" • log_processamento: relatório de processamento")
# === VERIFICAÇÃO FINAL ===
print("-" * 50)
print("🔍 Verificação final...")
# Ler de volta para confirmar
gdf_verificacao = gpd.read_file(arquivo_saida, layer='dados_consolidados')
print(f"✅ Verificação: {len(gdf_verificacao)} features lidas do GeoPackage")
return gdf_final, df_log
except Exception as e:
print(f"❌ Erro durante a consolidação: {e}")
return None, None
# === FUNÇÃO AUXILIAR PARA ANÁLISE ===
def analisar_geopackage(arquivo_gpkg):
"""
Analisa o conteúdo de um GeoPackage gerado
"""
try:
# Listar camadas disponíveis
import fiona
with fiona.open(arquivo_gpkg) as f:
camadas = fiona.listlayers(arquivo_gpkg)
print(f"📦 Análise do GeoPackage: {arquivo_gpkg}")
print(f"🗂️ Camadas disponíveis: {camadas}")
for camada in camadas:
try:
if camada == 'dados_consolidados':
gdf = gpd.read_file(arquivo_gpkg, layer=camada)
print(f"\n📊 Camada '{camada}':")
print(f" 📈 Features: {len(gdf)}")
print(f" 🗺️ CRS: {gdf.crs}")
print(f" 📋 Colunas: {list(gdf.columns)}")
print(f" 📐 Bounds: {gdf.total_bounds}")
# Análise por arquivo de origem
if 'arquivo_origem' in gdf.columns:
contagem = gdf['arquivo_origem'].value_counts()
print(f" 📁 Features por arquivo:")
for arquivo, count in contagem.items():
print(f" {arquivo}: {count}")
except Exception as e:
print(f" ❌ Erro ao analisar camada {camada}: {e}")
except Exception as e:
print(f"❌ Erro ao analisar GeoPackage: {e}")
# === EXECUTAR O LABORATÓRIO ===
if __name__ == "__main__":
# Executar consolidação
gdf_resultado, df_log = consolidar_shapefiles_para_geopackage()
if gdf_resultado is not None:
print("\n" + "="*50)
print("🎉 LABORATÓRIO CONCLUÍDO COM SUCESSO!")
print("="*50)
# Análise do resultado
analisar_geopackage('saidas/dados_consolidados.gpkg')
print("\n💡 Próximos passos sugeridos:")
print(" 1. Abra o arquivo .gpkg no QGIS para visualização")
print(" 2. Use o GeoDataFrame resultado para análises espaciais")
print(" 3. Experimente diferentes projeções conforme sua área de estudo")
else:
print("\n❌ Laboratório não foi concluído. Verifique os erros acima.")
5.3 Como executar o laboratório
Passo 1: Organize seus shapefiles na estrutura de pastas sugerida
Passo 2: Salve o script acima como scripts/consolidar_shapefiles.py
Passo 3: Execute o script a partir da raiz do projeto
cd projeto_geo
python scripts/consolidar_shapefiles.py
5.4 Resultados esperados
Após a execução bem-sucedida, você terá:
- Um arquivo GeoPackage unificado em
saidas/dados_consolidados.gpkg - Todas as geometrias reprojetadas para o sistema UTM especificado
- Metadados de origem preservados em cada feature
- Log de processamento como camada adicional no GeoPackage
- Relatório detalhado no console sobre todo o processo
6. Conclusão e próximos passos
Este capítulo forneceu uma base sólida para trabalhar com dados tabulares e geoespaciais em Python. Com Pandas e GeoPandas você agora domina:
- Instalação e configuração das bibliotecas essenciais usando pip
- Leitura e manipulação de dados tabulares sem escrever laços repetitivos
- Trabalho com arquivos espaciais, conversão de formatos e reprojeção de coordenadas
- Transformação de DataFrames em GeoDataFrames espaciais a partir de coordenadas
- Junções espaciais para cruzar dados de pontos com polígonos
- Operações geométricas como buffers para análises de proximidade
- Fluxos automatizados de consolidação de múltiplos arquivos espaciais
- Boas práticas de organização de projetos com estrutura de pastas padronizada
O laboratório prático demonstrou como aplicar todos esses conceitos em um cenário real, consolidando múltiplos shapefiles em um formato moderno e eficiente. Essa habilidade é fundamental para projetos de geoprocessamento que lidam com dados de múltiplas fontes.
No próximo capítulo, utilizaremos esses GeoDataFrames e dados processados para criar visualizações interativas com Folium, transformando análises em mapas navegáveis que comunicam resultados de forma efetiva.